package com.gitee.wsl.func.filter.bloomfilter.api

import com.gitee.wsl.ext.base.Arrays.copyOf
import com.gitee.wsl.func.filter.bloomfilter.bean.BitMaps
import com.gitee.wsl.func.filter.bloomfilter.bean.BitMaps.newBitMap
import com.gitee.wsl.func.filter.bloomfilter.predicate.CountingLongPredicate
import com.gitee.wsl.func.predicate.LongBiPredicate
import com.gitee.wsl.func.predicate.LongPredicate


/**
 * Produces bit map longs for a Bloom filter.
 *
 *
 * Each bit map is a little-endian long value representing a block of bits of in a filter.
 *
 *
 *
 * The returned array will have length `ceil(m / 64)` where `m` is the number of bits in the filter and `ceil` is the ceiling function. Bits
 * 0-63 are in the first long. A value of 1 at a bit position indicates the bit index is enabled.
 *
 *
 *
 * *The default implementations of the `makePredicate()` and `asBitMapArray` methods are slow and should be reimplemented in the implementing
 * classes where possible.*
 *
 *
 * @since 4.5.0-M2
 */
fun interface BitMapExtractor {
    /**
     * Return a copy of the BitMapExtractor data as a bit map array.
     *
     *
     * The default implementation of this method is slow. It is recommended
     * that implementing classes reimplement this method.
     *
     * @return An array of bit map data.
     */
    fun asBitMapArray(): LongArray {
        class Bits {
            private var data = LongArray(16)
            private var size = 0

            fun add(bits: Long): Boolean {
                if (size == data.size) {
                    // This will throw an out-of-memory error if there are too many bits.
                    // Since bits are addressed using 32-bit signed integer indices
                    // the maximum length should be ~2^31 / 2^6 = ~2^25.
                    // Any more is a broken implementation.
                    data = copyOf(data, size * 2)
                }
                data[size++] = bits
                return true
            }

            fun toArray(): LongArray {
                // Edge case to avoid a large array copy
                return if (size == data.size) data else copyOf(data, size)
            }
        }

        val bits = Bits()
        processBitMaps(LongPredicate { bit: Long -> bits.add(bit) })
        return bits.toArray()
    }

    /**
     * Applies the `func` to each bit map pair in order. Will apply all of the bit maps from the other BitMapExtractor to this extractor. If this
     * extractor does not have as many bit maps it will provide 0 (zero) for all excess calls to the LongBiPredicate.
     *
     *
     * *The default implementation of this method uses `asBitMapArray()`. It is recommended that implementations of BitMapExtractor that have local
     * arrays reimplement this method.*
     *
     *
     * @param other The other BitMapExtractor that provides the y values in the (x,y) pair.
     * @param func  The function to apply.
     * @return A LongPredicate that tests this BitMapExtractor's bitmap values in order.
     */
    fun processBitMapPairs(other: BitMapExtractor, func: LongBiPredicate): Boolean {
        val p = CountingLongPredicate(asBitMapArray(), func)
        return other.processBitMaps(p) && p.processRemaining()
    }

    /**
     * Each bit map is passed to the predicate in order. The predicate is applied to each
     * bit map value, if the predicate returns `false` the execution is stopped, `false`
     * is returned, and no further bit maps are processed.
     *
     *
     * If the extractor is empty this method will return true.
     *
     *
     * Any exceptions thrown by the action are relayed to the caller.
     *
     * @param predicate the function to execute
     * @return `true` if all bit maps returned `true`, `false` otherwise.
     * @throws NullPointerException if the specified consumer is null
     */
    fun processBitMaps(predicate: LongPredicate): Boolean

    companion object {
        /**
         * Creates a BitMapExtractor from an array of Long.
         *
         * @param bitMaps the bit maps to return.
         * @return a BitMapExtractor.
         */
        fun fromBitMapArray(vararg bitMaps: Long): BitMapExtractor {
            return object : BitMapExtractor {
                override fun asBitMapArray(): LongArray {
                    return copyOf(bitMaps, bitMaps.size)
                }

                override fun processBitMapPairs(other: BitMapExtractor, func: LongBiPredicate): Boolean {
                    val p = CountingLongPredicate(bitMaps, func)
                    return other.processBitMaps(p) && p.processRemaining()
                }

                override fun processBitMaps(predicate: LongPredicate): Boolean {
                    for (word in bitMaps) {
                        if (!predicate.test(word)) {
                            return false
                        }
                    }
                    return true
                }
            }
        }

        /**
         * Creates a BitMapExtractor from an IndexExtractor.
         *
         * @param extractor the IndexExtractor that specifies the indexes of the bits to enable.
         * @param numberOfBits the number of bits in the Bloom filter.
         * @return A BitMapExtractor that produces the bit maps equivalent of the Indices from the extractor.
         */
        fun fromIndexExtractor(extractor: IndexExtractor, numberOfBits: Int): BitMapExtractor {
            val result = newBitMap(numberOfBits)
            extractor.processIndices( { i: Int ->
                BitMaps.set(result, i)
                true
            })
            return fromBitMapArray(*result)
        }
    }
}
